#include "eeprom_device.hpp"
#include "apm32a10x_gpio.h"
#include "apm32a10x_i2c.h"
#include "system.h"
#include "stdio.h"
#include "apm32a10x_misc.h"
class __M24C64_Portable : public M24C64::Portable{
public:
    virtual void busInit(void);
    virtual unsigned char readByte(unsigned int address);
    virtual void writeByte(unsigned int address,unsigned char byte);
};

void iic_delay(){

}

static GPIO_T *port_scl = GPIOB;
static GPIO_PIN_T pin_scl = GPIO_PIN_6;
static GPIO_PIN_T pin_sda = GPIO_PIN_7;

/*********************************New Add*********************************/
void IIC_SCL_0(){GPIO_ResetBit(port_scl,pin_scl);}
void IIC_SCL_1(){GPIO_SetBit(port_scl,pin_scl);}
void IIC_SDA_0(){GPIO_ResetBit(port_scl,pin_sda);}
void IIC_SDA_1(){GPIO_SetBit(port_scl,pin_sda);}
uint8_t READ_SDA(){return GPIO_ReadInputBit(port_scl,pin_sda);}

void i2c_NAck(void){   
    IIC_SDA_1();	
	iic_delay();
	IIC_SCL_1();	
	iic_delay();
	IIC_SCL_0();
	iic_delay();	
}

void i2c_ACK(void){
	IIC_SDA_0();
	IIC_SCL_1();
	iic_delay();
	IIC_SCL_0();
	IIC_SDA_1();
}

void i2c_Start(void){
	IIC_SDA_1();	  	  
	iic_delay();
	IIC_SCL_1();
	iic_delay();
	IIC_SDA_0();
}

uint8_t i2c_WaitAck(void){
    uint8_t AckFlag;
    uint8_t uiVal = 0;  
	IIC_SCL_0();
	iic_delay();    
	IIC_SDA_1();
	IIC_SCL_1();
	iic_delay();
    AckFlag = 1;
	while(uiVal < 255){
        if(READ_SDA()==0){
		    AckFlag = 0;
		    break;
	    }uiVal++;
    }
	IIC_SCL_0();  
	return AckFlag;		
}

void i2c_Stop(void){
	IIC_SCL_0();
	iic_delay();
	IIC_SDA_0();
	iic_delay();
	IIC_SCL_1(); 
	iic_delay();
	IIC_SDA_1();
	iic_delay();
}

uint8_t i2c_ReadByte(uint8_t ack){
	uint8_t i;
	uint8_t value;
	value = 0;
	for (i = 0; i < 8; i++){
		value <<= 1;
		IIC_SCL_1();
		iic_delay();
		if (READ_SDA()){value++;}
		IIC_SCL_0();
	    iic_delay();
	}
	if(ack==0) i2c_NAck();
	else i2c_ACK();
	return value;
}

void i2c_SendByte(uint8_t uByteVal){
    uint8_t iCount;
    for(iCount = 0;iCount < 8;iCount++){  
        IIC_SCL_0();
		iic_delay();		
		if((uByteVal & 0x80) >> 7) IIC_SDA_1();
		else IIC_SDA_0();	
		iic_delay();        
        IIC_SCL_1();
		iic_delay();
		uByteVal <<= 1;
    } 
		IIC_SCL_0();
}

unsigned char IIC_rec(void){
	int i;
	char tmp=0;
	for(i=0;i<8;i++){
		tmp <<=1;
		tmp	|=READ_SDA();
		IIC_SCL_1();
		iic_delay();
		IIC_SCL_0();
	}
	return tmp;
}

void __M24C64_Portable::busInit(void){
    GPIO_Config_T gpio_configuration;
    System_setPeripheralClockEnable(CLK_GPIOB,TRUE);
    gpio_configuration.pin = pin_scl | pin_sda;
    gpio_configuration.mode = GPIO_MODE_OUT_OD;
    gpio_configuration.speed = GPIO_SPEED_50MHz;
    GPIO_Config(port_scl,&gpio_configuration);
}

unsigned char __M24C64_Portable::readByte(unsigned int address){
    unsigned char rec_data = 0;
    i2c_Start();
    i2c_SendByte(0xA0);
    if(i2c_WaitAck()){
        i2c_Stop();
        printf("failed");
        return 1;
    }
    i2c_SendByte((address>>8) & 0xFF);
    i2c_WaitAck();
    i2c_SendByte(address & 0xFF);
    i2c_WaitAck();

    i2c_Start();
    i2c_SendByte(0xA0+1);    
    if(i2c_WaitAck()){
        i2c_Stop();
        printf("failed");
    return 1;
    }
    rec_data = IIC_rec();
    i2c_Stop();
    return rec_data;
}
void __M24C64_Portable::writeByte(unsigned int address,unsigned char byte){
    i2c_Start();
    i2c_SendByte(0xA0);    
    if(i2c_WaitAck()){
        i2c_Stop();
        printf("0xA0 failed\r\n");
    return;
    }
    i2c_SendByte((address>>8) & 0xFF);
    if(i2c_WaitAck()){
        i2c_Stop();
        printf("address>>8 failed\r\n");
    }
    i2c_SendByte(address & 0xFF);
    if(i2c_WaitAck()){
        i2c_Stop();
        printf("address & 0xFF failed\r\n");
    }
    i2c_SendByte(byte);
    if(i2c_WaitAck()){
        i2c_Stop();
        printf("byte failed\r\n");
    }    
    i2c_Stop();
}

// void __M24C64_Portable::busInit(void){
//     GPIO_Config_T gpio_configuration;
//     I2C_Config_T i2c_configuration;
//     System_setPeripheralClockEnable(CLK_GPIOB,TRUE);
//     System_setPeripheralClockEnable(CLK_I2C1,TRUE);

//     gpio_configuration.pin = pin_scl | pin_sda;
//     gpio_configuration.mode = GPIO_MODE_AF_OD;
//     gpio_configuration.speed = GPIO_SPEED_50MHz;
//     GPIO_Config(port_scl,&gpio_configuration);

//     i2c_configuration.clockSpeed = 10000;
//     i2c_configuration.mode = I2C_MODE_I2C;
//     i2c_configuration.dutyCycle = I2C_DUTYCYCLE_2;
//     i2c_configuration.ownAddress1 = 0xa0;
//     i2c_configuration.ack = I2C_ACK_ENABLE;
//     i2c_configuration.ackAddress = I2C_ACK_ADDRESS_7BIT;
//     I2C_Config(I2C1,&i2c_configuration);

//     I2C_Enable(I2C1);
//     I2C_EnableAcknowledge(I2C1);
//     I2C_EnableInterrupt(I2C1,I2C_INT_EVT);
// }

// unsigned char __M24C64_Portable::readByte(unsigned int address){
//     uint8_t data;
//     while ( I2C_ReadStatusFlag(I2C1,I2C_FLAG_BUSBSY) != RESET );
//     I2C_EnableGenerateStart(I2C1);
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_MODE_SELECT) );
//     I2C_Tx7BitAddress(I2C1,0xa0,I2C_DIRECTION_TX);
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) );
//     I2C_TxData(I2C1,((uint8_t)address >> 8));   /* Send high 8-bit address. */
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) );
//     I2C_TxData(I2C1,((uint8_t)address & 0xff));
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) );
//     I2C_EnableGenerateStart(I2C1);
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_MODE_SELECT) );
//     I2C_Tx7BitAddress(I2C1,0xa0,I2C_DIRECTION_RX);
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) );
//     while ( I2C_ReadStatusFlag(I2C1,I2C_FLAG_RXBNE) == RESET );
//     I2C_EnableAcknowledge(I2C1);
//     data = I2C_RxData(I2C1);
//     I2C_EnableGenerateStop(I2C1);
//     return data;
// }

// void __M24C64_Portable::writeByte(unsigned int address,unsigned char byte){
//     while ( I2C_ReadStatusFlag(I2C1,I2C_FLAG_BUSBSY) != RESET );
//     I2C_EnableGenerateStart(I2C1);
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_MODE_SELECT) );
//     I2C_Tx7BitAddress(I2C1,0xa0,I2C_DIRECTION_TX);
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) );
//     I2C_TxData(I2C1,((uint8_t)address >> 8));
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) );
//     I2C_TxData(I2C1,((uint8_t)address & 0xff));
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) );
//     if ( I2C_ReadStatusFlag(I2C1,I2C_FLAG_TXBE) != RESET ){
//         I2C_TxData(I2C1,byte);
//     }
//     while ( !I2C_ReadEventStatus(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) );
//     I2C_EnableGenerateStop(I2C1);
// }

static __M24C64_Portable *m24c64_portable = nullptr;
static M24C64 *eeprom = nullptr;

M24C64 *getEEPROM(void){
    if ( eeprom == nullptr ){
        m24c64_portable = new __M24C64_Portable();
        if ( m24c64_portable == nullptr ) return nullptr;
        m24c64_portable->busInit();
        eeprom = new M24C64(m24c64_portable);
    }
    return eeprom;
}